#include "DataObject.h"
#include "Share.h"

/* Implementation of the IDataObject COM interface */
DataObject::DataObject(IDataObject* core, std::function<void(const char*, DWORD)> comp, LPCSTR share)
    : m_lRefCount(1),
      m_core(core),
      m_inOp(VARIANT_FALSE),
      m_supportAsync(VARIANT_TRUE),
      m_share(share),
      m_sendCompletion(comp) { }

DataObject::~DataObject()
{
    m_core->Release();
    if (!m_share.empty()) {
        m_sendCompletion(m_share.c_str(), CBSH_FILES_FINISH);
    }
}

ULONG __stdcall DataObject::AddRef(void)
{
    return InterlockedIncrement(&m_lRefCount);
}

ULONG __stdcall DataObject::Release(void)
{
    LONG count = InterlockedDecrement(&m_lRefCount);
    if (count == 0) {
        delete this;
        return 0;
    } else {
        return count;
    }
}

HRESULT __stdcall DataObject::QueryInterface(REFIID iid, void** ppvObject)
{
    if (iid == IID_IDataObject || iid == IID_IUnknown) {
        *ppvObject = dynamic_cast<IDataObject*>(this);
    } else if (iid == IID_IDataObjectAsyncCapability) {
        *ppvObject = dynamic_cast<IDataObjectAsyncCapability*>(this);
    } else {
        *ppvObject = nullptr;
        return E_NOINTERFACE;
    }
    AddRef();
    return S_OK;
}

HRESULT __stdcall DataObject::GetData(FORMATETC* pFormatEtc,
                                      STGMEDIUM* pMedium)
{
    return m_core->GetData(pFormatEtc, pMedium);
}

HRESULT __stdcall DataObject::GetDataHere(FORMATETC* pFormatEtc,
                                          STGMEDIUM* pMedium)
{
    return m_core->GetDataHere(pFormatEtc, pMedium);
}

HRESULT __stdcall DataObject::QueryGetData(FORMATETC* pFormatEtc)
{
    return m_core->QueryGetData(pFormatEtc);
}

HRESULT __stdcall DataObject::GetCanonicalFormatEtc(FORMATETC* pFormatEct,
                                                    FORMATETC* pFormatEtcOut)
{
    return m_core->GetCanonicalFormatEtc(pFormatEct, pFormatEtcOut);
}

HRESULT __stdcall DataObject::SetData(FORMATETC* pFormatEtc,
                                      STGMEDIUM* pMedium, BOOL fRelease)
{
    return m_core->SetData(pFormatEtc, pMedium, fRelease);
}

HRESULT __stdcall DataObject::EnumFormatEtc(DWORD dwDirection,
                                            IEnumFORMATETC** ppEnumFormatEtc)
{
    return m_core->EnumFormatEtc(dwDirection, ppEnumFormatEtc);
}

HRESULT __stdcall DataObject::DAdvise(FORMATETC* pFormatEtc, DWORD advf,
                                      IAdviseSink* pAdvSink,
                                      DWORD* pdwConnection)
{
    return m_core->DAdvise(pFormatEtc, advf, pAdvSink, pdwConnection);
}

HRESULT __stdcall DataObject::DUnadvise(DWORD dwConnection)
{
    return m_core->DUnadvise(dwConnection);
}

HRESULT __stdcall DataObject::EnumDAdvise(IEnumSTATDATA** ppEnumAdvise)
{
    return m_core->EnumDAdvise(ppEnumAdvise);
}

HRESULT __stdcall DataObject::StartOperation(IBindCtx* pbcReserved)
{
    m_inOp = VARIANT_TRUE;
    return S_OK;
}

HRESULT __stdcall DataObject::InOperation(BOOL* pfInAsyncOp)
{
    *pfInAsyncOp = m_inOp;
    return S_OK;
}
HRESULT __stdcall DataObject::EndOperation(HRESULT hResult, IBindCtx* pbcReserved, DWORD dwEffects)
{
    m_inOp = VARIANT_FALSE;
    Release();
    return S_OK;
}

HRESULT __stdcall DataObject::GetAsyncMode(BOOL* pfIsOpAsync)
{
    *pfIsOpAsync = m_supportAsync;
    return S_OK;
}

HRESULT __stdcall DataObject::SetAsyncMode(BOOL fDoOpAsync)
{
    m_supportAsync = fDoOpAsync;
    return S_OK;
}